package com.example.tumasstest;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.recyclerview.widget.LinearLayoutManager;

import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.content.ContentResolver;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;

import com.example.tumasstest.databinding.ActivityMainBinding;
import com.vois.jack.btmgr.blebase.BleConstant;
import com.vois.jack.btmgr.blebase.BleDevCommonMsg;
import com.vois.jack.btmgr.blebase.BleDevMgr;
import com.vois.jack.btmgr.blebase.BleDevice;
import com.vois.jack.btmgr.blebase.BleDeviceBuilder;
import com.vois.jack.btmgr.blebase.BleScanResult;
import com.vois.jack.btmgr.common.DeviceRegisterHelper;
import com.vois.jack.btmgr.devices.WLBleDataDevice.WLBleDataDevice;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getName();
    private static final int DEBUG_INFO = 449;
    private static final int DEVICE_CONNECTED = 464;
    private static final int SCAN_FIND_BLE_DEVICE = 465;
    private static final int SCAN_FIND_BLE_BATCH_DEVICES = 962;
    private static final int SCAN_STARTING = 380;

    private static final int SCAN_AUTO_STOP = 381;
    private static final int BLE_DEVICE_READY = 342;
    private static final int DEVICE_DISCONNECTED = 740;

    private static final int GET_SEQ_ID_RET = 403;
    private static final int GET_VENDOR_RET = 815;

    private static final int GET_BLE_DATA_IND = 44;
    private static final int BLE_DATA_SEND_ACK = 956;

    ActivityMainBinding binding;
    MainActivityHandler handler;
    BleDeviceAdapter bleDeviceAdapter;
    BleDevMgr bleDevMgr;
    private boolean isScanning;
    private boolean isConnected;
    private boolean isConnecting;
    private BleDevice bleDevice;

    private ByteBuffer fileBuffer;

    private boolean isDebug = true;

    class MainActivityHandler extends Handler {
        MainActivity activity;

        public MainActivityHandler(MainActivity activity) {
            super();
            this.activity = activity;
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case DEVICE_DISCONNECTED: {
                    isConnecting = false;
                    isConnected = false;
                    activity.binding.btnBle.setEnabled(true);
                    activity.binding.btnBle.setText("Scan");

                    binding.btnSend.setEnabled(false);
                    binding.edtSendData.setEnabled(false);

                    activity.binding.recycleView.setVisibility(View.VISIBLE);
                    activity.binding.ctLayerInfo.setVisibility(View.INVISIBLE);
                }
                break;

                case SCAN_AUTO_STOP: {
                    binding.btnBle.setText("Scan");
                    isScanning = false;

                    bleDeviceAdapter.clearData();
                    binding.recycleView.removeAllViews();
                }
                break;

                case DEVICE_CONNECTED:
                    isConnected = true;
                    isConnecting = false;

                    activity.binding.btnBle.setEnabled(true);
                    activity.binding.btnBle.setText("Disconnect");

                    if (activity.binding.recycleView.getChildCount() > 0) {
                        activity.binding.recycleView.removeAllViews();
                        activity.bleDeviceAdapter.clearData();
                    }
                    break;

                case DEBUG_INFO:
                    activity.setDebugInfo((String) msg.obj);
                    break;

                case SCAN_FIND_BLE_DEVICE: {
                    if (activity.isScanning) {
                        BleDeviceAdapter.BleScanItem item = (BleDeviceAdapter.BleScanItem) msg.obj;
                        activity.bleDeviceAdapter.addItem(item.device, item.rssi);
                    }
                }
                break;

                case SCAN_STARTING: {
                    activity.binding.recycleView.setVisibility(View.VISIBLE);
                }
                break;

                case BLE_DEVICE_READY: {
                    activity.bleDeviceAdapter.clearData();
                    activity.binding.recycleView.setVisibility(View.INVISIBLE);
                    activity.binding.ctLayerInfo.setVisibility(View.VISIBLE);

                    if (isDebug) {
                        activity.binding.txSeq.setText("12345678");
                        activity.binding.txVendor.setText("Simulator");
                        binding.btnSend.setEnabled(true);
                        binding.edtSendData.setEnabled(true);
                    }else {
                        activity.bleDevice = activity.bleDevMgr.getBleDevice((BluetoothDevice) msg.obj);
                        activity.bleDevice.getValidationData();
                        activity.bleDevice.getVendor();

                        if (bleDevice instanceof WLBleDataDevice) {
                            binding.btnSend.setEnabled(true);
                            binding.edtSendData.setEnabled(true);
                        }
                    }
                }
                break;

                case SCAN_FIND_BLE_BATCH_DEVICES: {
                    if (activity.isScanning) {
                        List<BleDeviceAdapter.BleScanItem> list = (List<BleDeviceAdapter.BleScanItem>) msg.obj;
                        for (BleDeviceAdapter.BleScanItem item : list) {
                            activity.bleDeviceAdapter.addItem(item.device, item.rssi);
                        }
                    }
                }
                break;

                case GET_SEQ_ID_RET: {
                    activity.binding.txSeq.setText((CharSequence) msg.obj);
                }
                break;

                case GET_VENDOR_RET: {
                    activity.binding.txVendor.setText((CharSequence) msg.obj);
                }
                break;

                case GET_BLE_DATA_IND: {
                    Bundle bundle = msg.getData();
                    byte[] data = bundle.getByteArray(BleConstant.EXTRA_DATA_TYPE);
                    activity.setDebugInfo("BLE data: " + Arrays.toString(data));
                    activity.setDebugInfo("data Len:" + data.length);
                    if (data.length <= 100) {
                        activity.setDebugInfo("TXT:" + new String(data));
                    }else {
                        Bitmap  bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                        activity.binding.picView.setImageBitmap(bitmap);
                    }
                }
                break;

                case BLE_DATA_SEND_ACK: {
                    Bundle bundle = msg.getData();
                    boolean result = bundle.getBoolean(BleConstant.EXTRA_VALUE_RESULT);
                    long id = bundle.getLong(BleConstant.EXTRA_MSG_ID);
                    activity.setDebugInfo("Send ack: " + result + ", id: " + id);
                }
                break;
            }
        }
    }

    protected void showInfo(String info) {
        handler.sendMessage(handler.obtainMessage(DEBUG_INFO, info + "\n"));
    }

    public static byte[] int2byte(int[] src) {
        int srcLength = src.length;
        byte[]dst = new byte[srcLength << 2];

        for (int i=0; i<srcLength; i++) {
            int x = src[i];
            int j = i << 2;
            dst[j++] = (byte) ((x >>> 0) & 0xff);
            dst[j++] = (byte) ((x >>> 8) & 0xff);
            dst[j++] = (byte) ((x >>> 16) & 0xff);
            dst[j++] = (byte) ((x >>> 24) & 0xff);
        }
        return dst;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        handler = new MainActivityHandler(this);

        if ((ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != 0)
                || (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != 0)) {
            ActivityCompat.requestPermissions(this, new String[]{
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.BLUETOOTH_CONNECT,
                    Manifest.permission.BLUETOOTH_SCAN,
                    Manifest.permission.BLUETOOTH_ADMIN,
                    Manifest.permission.CAMERA
            }, 1);
        }

        DeviceRegisterHelper.registerWithProfile(null);
        BleDeviceBuilder.registerBleDeviceClass("tumass.*", WLBleDataDevice.class, 0, true);
        bleDevMgr = new BleDevMgr(this, new BleDevMgr.BleMgrCallback() {
            @Override
            public void onStarted() {
            }

            @Override
            public void onStopped() {
            }

            @Override
            public void onBluetoothOn() {
            }

            @Override
            public void onBluetoothOff() {
            }
        });

        bleDevMgr.startBleManager();

        binding.btnSend.setEnabled(false);
        binding.edtSendData.setEnabled(false);
        binding.btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!isDebug) {
                    if (bleDevice instanceof WLBleDataDevice) {
                        ((WLBleDataDevice) bleDevice).sendData(binding.edtSendData.getText().toString().getBytes());
                    }
                }else {
                    ((SimWLBleDataDevice) bleDevice).sendData(binding.edtSendData.getText().toString().getBytes());
                }
            }
        });

        ActivityResultLauncher launcher = registerForActivityResult(new ActivityResultContracts.GetContent(), new ActivityResultCallback<Uri>() {
            @Override
            public void onActivityResult(Uri o) {
                Log.d(TAG, "onActivityResult: " + o);
                try {
                    ContentResolver contentResolver = getContentResolver();
                    binding.picView.setImageURI(o);
                    byte[] buffer;
                    InputStream inputStream = contentResolver.openInputStream(o);
                    assert inputStream != null;
                    buffer = new byte[inputStream.available()];
                    inputStream.read(buffer);
                    inputStream.close();
                    if (!isDebug) {
                        if (bleDevice != null && bleDevice instanceof WLBleDataDevice) {
                            ((WLBleDataDevice) bleDevice).sendData(buffer);
                        }
                    }else {
                        ((SimWLBleDataDevice) bleDevice).sendData(buffer);
                    }
                }catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        binding.btnSendPic.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                launcher.launch("image/*");
//                InputStream inputStream = null;
//                try {
//                    byte[] buffer;
//                    inputStream = getResources().openRawResource(R.raw.logo);
//                    buffer = new byte[inputStream.available()];
//                    inputStream.read(buffer);
//                    inputStream.close();
//
//                    Log.d(TAG, "Pic data length: " + buffer.length);
//
//                    if (!isDebug) {
//                        if (bleDevice != null && bleDevice instanceof WLBleDataDevice) {
//                            ((WLBleDataDevice) bleDevice).sendData(buffer);
//                        }
//                    }else {
//                        ((SimWLBleDataDevice) bleDevice).sendData(buffer);
//                    }
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
            }
        });

        bleDeviceAdapter = new BleDeviceAdapter();
        bleDeviceAdapter.setOnItemClickListener(new BleDeviceAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(int position) {
                BluetoothDevice device = bleDeviceAdapter.getDevice(position);
                String name = null;

                try {
                    name = device.getName();
                } catch (SecurityException e) {
                    e.printStackTrace();
                    name = "null";
                }

                binding.btnBle.setEnabled(false);
                binding.btnBle.setText("Connecting...");
                bleDevMgr.stopScanBleDevice();
                isConnecting = true;

                bleDevMgr.openBleDevice(device, name, null, new BleDevice.BleDeviceListener() {
                    @Override
                    public void onConnecting(BluetoothDevice device) {
                        Log.d(TAG, "onConnecting: " + device.getName());
                        showInfo("Ble is connecting...");
                    }

                    @Override
                    public void onConnected(BluetoothDevice device) {
                        Log.d(TAG, "onConnected: " + device.getName());
                        showInfo("Ble is connected");
                        handler.sendEmptyMessage(DEVICE_CONNECTED);
                    }

                    @Override
                    public void onDisconnected(BluetoothDevice device) {
                        showInfo("Ble is disconnected");
                    }

                    @Override
                    public void onMessage(BluetoothDevice device, Message msg) {
                        showInfo("ble got message:" + Integer.toHexString(msg.what));
                        BleDevCommonMsg bleDevCommonMsg = BleDevCommonMsg.valueOf(msg.what);
                        try {
                            if (device != null) {
                                if (device.getName() != null) {
                                    showInfo("onMessage: " + device.getName() + " msg:" + bleDevCommonMsg);
                                } else {
                                    showInfo("onMessage: " + device.getAddress() + " msg:" + bleDevCommonMsg);
                                }
                            }
                        } catch (SecurityException e) {
                            e.printStackTrace();
                        }

                        switch (bleDevCommonMsg) {
                            case BLE_DEV_COMMON_GET_VENDOR_RESULT: {
                                Bundle bundle = msg.getData();
                                if (bundle != null) {
                                    String vendor = bundle.getString(BleConstant.EXTRA_VENDORID);
                                    Message message = handler.obtainMessage();
                                    message.what = GET_VENDOR_RET;
                                    message.obj = vendor;
                                    handler.sendMessage(message);
                                }
                            }
                            break;

                            case BLE_DEV_COMMON_VALIDATION_RESULT: {
                                Bundle bundle = msg.getData();
                                if (bundle != null) {
                                    String seq = bundle.getString(BleConstant.EXTRA_SEQID);
                                    Message message = handler.obtainMessage();
                                    message.what = GET_SEQ_ID_RET;
                                    message.obj = seq;
                                    handler.sendMessage(message);
                                }
                            }
                            break;

                            case BLE_DEV_COMMON_DATA_RECV_IND: {
                                Bundle bundle = msg.getData();
                                Message message = handler.obtainMessage();
                                message.what = GET_BLE_DATA_IND;
                                message.setData(bundle);
                                handler.sendMessage(message);
                            }
                            break;

                            case BLE_DEV_COMMON_DATA_SEND_DATA_ACK: {
                                Bundle bundle = msg.getData();
                                Message message = handler.obtainMessage();
                                message.what = BLE_DATA_SEND_ACK;
                                message.setData(bundle);
                                handler.sendMessage(message);
                            }
                            break;

                            case BLE_DEV_DEBUG_MSG: {
                                Bundle bundle = msg.getData();
                                showInfo(bundle.getString("INFO"));
                            }
                            break;
                        }
                    }

                    @Override
                    public void onClosed(BluetoothDevice device) {
                        showInfo("Ble is closed");
                        handler.sendEmptyMessage(DEVICE_DISCONNECTED);
                    }

                    @Override
                    public void onError(BluetoothDevice device, int errorCause) {
                        showInfo("Ble error: " + errorCause);
                    }

                    @Override
                    public void onReady(BluetoothDevice device) {
                        showInfo("Ble is ready");
                        Message message = handler.obtainMessage();
                        message.what = BLE_DEVICE_READY;
                        message.obj = device;
                        handler.sendMessage(message);
                    }

                    @Override
                    public void onRssi(BluetoothDevice device, int rssi) {

                    }
                });
            }
        });
        binding.recycleView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        binding.recycleView.setAdapter(bleDeviceAdapter);

        binding.btnBle.setOnClickListener(v -> {
            if (isDebug) {
                bleDevice = new SimWLBleDataDevice();
                bleDevice.setListener(new BleDevice.BleDeviceListener() {
                    @Override
                    public void onConnecting(BluetoothDevice device) {

                    }

                    @Override
                    public void onConnected(BluetoothDevice device) {
                        showInfo("Ble is connected");
                        handler.sendEmptyMessage(DEVICE_CONNECTED);
                    }

                    @Override
                    public void onDisconnected(BluetoothDevice device) {

                    }

                    @Override
                    public void onMessage(BluetoothDevice device, Message msg) {
                        BleDevCommonMsg bleDevCommonMsg = BleDevCommonMsg.valueOf(msg.what);

                        switch (bleDevCommonMsg) {
                            case BLE_DEV_COMMON_DATA_RECV_IND: {
                                Bundle bundle = msg.getData();
                                Message message = handler.obtainMessage();
                                message.what = GET_BLE_DATA_IND;
                                message.setData(bundle);
                                handler.sendMessage(message);
                            }
                            break;

                            case BLE_DEV_COMMON_DATA_SEND_DATA_ACK: {
                                Bundle bundle = msg.getData();
                                Message message = handler.obtainMessage();
                                message.what = BLE_DATA_SEND_ACK;
                                message.setData(bundle);
                                handler.sendMessage(message);
                            }
                            break;

                            case BLE_DEV_DEBUG_MSG: {
                                Bundle bundle = msg.getData();
                                showInfo(bundle.getString("INFO"));
                            }
                            break;
                        }
                    }

                    @Override
                    public void onClosed(BluetoothDevice device) {
                        showInfo("Ble is closed");
                        handler.sendEmptyMessage(DEVICE_DISCONNECTED);
                    }

                    @Override
                    public void onError(BluetoothDevice device, int errorCause) {

                    }

                    @Override
                    public void onReady(BluetoothDevice device) {
                        showInfo("Ble is ready");
                        Message message = handler.obtainMessage();
                        message.what = BLE_DEVICE_READY;
                        message.obj = device;
                        handler.sendMessage(message);
                    }

                    @Override
                    public void onRssi(BluetoothDevice device, int rssi) {

                    }
                });
                bleDevice.openBleDevice(false);
                return;
            }

            if (isConnecting) {
                return;
            }

            if (isConnected) {
                binding.btnBle.setEnabled(false);
                bleDevice.closeBleDevice();
                return;
            }

            if (isScanning) {
                binding.btnBle.setText("Scan");
                isScanning = false;
                bleDeviceAdapter.clearData();
                if (binding.recycleView.getChildCount() > 0) {
                    binding.recycleView.removeAllViews();
                }
                bleDevMgr.stopScanBleDevice();
            }else {
                binding.btnBle.setText("Stop");
                bleDevMgr.startScanBleDevice(null, null, 120000, new BleDevMgr.BleScanCallback() {
                    @Override
                    public void onStarting() {
                        showInfo("Starting scan...");
                        isScanning = true;
                        handler.sendMessage(handler.obtainMessage(SCAN_STARTING));
                    }

                    @Override
                    public void onScanFound(BluetoothDevice device, String name, byte[] rawScanData, int rssi) {
                        Message message = Message.obtain();
                        message.what = SCAN_FIND_BLE_DEVICE;
                        BleDeviceAdapter.BleScanItem item = new BleDeviceAdapter.BleScanItem();
                        item.rssi = rssi;
                        item.device = device;
                        message.obj = item;
                        handler.sendMessage(message);
                    }

                    @Override
                    public void onBatchScanResultFound(BleScanResult[] scanResults) {
                        List<BleDeviceAdapter.BleScanItem> list = new ArrayList<>();
                        for(BleScanResult result : scanResults) {
                            BleDeviceAdapter.BleScanItem item = new BleDeviceAdapter.BleScanItem();
                            item.rssi = result.getRssi();
                            item.device = result.getBluetoothDevice();
                            list.add(item);
                        }

                        Message message = Message.obtain();
                        message.what = SCAN_FIND_BLE_BATCH_DEVICES;
                        message.obj = list;
                        handler.sendMessage(message);
                    }

                    @Override
                    public void onScanFail() {
                        showInfo("Scan failed");
                        isScanning = false;
                    }

                    @Override
                    public void onScanStopped() {
                        showInfo("Scan stopped");
                        isScanning = false;
                    }

                    @Override
                    public void onScanTimeout() {
                        showInfo("Scan Timeout");
                        isScanning = false;
                        handler.sendEmptyMessage(SCAN_AUTO_STOP);
                    }
                });
            }
        });


        isConnected = false;
        isConnecting = false;
        isScanning = false;
    }

    public void setDebugInfo(String info) {
        binding.edtInfo.setMovementMethod(ScrollingMovementMethod.getInstance());
        binding.edtInfo.setSelection(binding.edtInfo.getText().length(), binding.edtInfo.getText().length());
        binding.edtInfo.append(info);
        binding.edtInfo.append("\n");

        int infoSize = binding.edtInfo.getText().length();
        if (infoSize > 8090) {
            binding.edtInfo.setText("");
        }
    }
}